package com.nhncorp.pleiades.demo;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.nhncorp.pleiades.client.LockService;
import com.nhncorp.pleiades.client.LockServiceClient;
import com.nhncorp.pleiades.client.PleiadesUser;
import com.nhncorp.pleiades.conf.PleiadesConfiguration;

public class ParallelServer {

  private static PleiadesConfiguration conf;
  String path1 = "/nhn/nebula/job#1";
  String path2 = "/nhn/nebula/job#2";
  String path3 = "/nhn/nebula/job#3";
  List<Job> jobList = null;

  private enum ServerType {
    FAST, NORMAL, SLOW
  };

  public ParallelServer() {
    try {
      conf = new PleiadesConfiguration();
      jobList = new ArrayList<Job>();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public class Job {

    private String jobName = "";

    private int numberOfTaskToBeDone = 0;

    public Job(String name) {
      this.jobName = name;
      numberOfTaskToBeDone = 10;
    }

    public int tasksLeft() {
      return numberOfTaskToBeDone;
    }

    synchronized public boolean doTask() {
      --numberOfTaskToBeDone;
      if (numberOfTaskToBeDone <= 0)
        return true;
      return false;
    }

    public String getJobName() {
      return this.jobName;
    }

  }

  public class TaskServer implements Runnable {

    private LockService p1eiades;
    private String identity = "";
    private ServerType serverType = null;
    private String jobPath = "";
    private boolean stopRequested = false;
    private Job job = null;

    public TaskServer(String id, ServerType type, String path) {
      try {
        job = new Job(path);
        jobList.add(job);
        this.identity = id;
        this.serverType = type;
        this.jobPath = path;
        p1eiades = new LockServiceClient(new PleiadesUser("default", id), conf);
      } catch (IOException e) {
        e.printStackTrace();
      }
    }

    public void run() {
      try {
        p1eiades.releaseLock(jobPath, LockService.LockMode.LOCK_EXCLUSIVE);
      } catch (IOException e) {
        e.printStackTrace();
      }
      loop();
    }

    private void loop() {
      while (!stopRequested) {
        try {
          if (this.serverType.equals(ServerType.FAST)) {
            Thread.sleep(1000);
          } else if (this.serverType.equals(ServerType.NORMAL)) {
            Thread.sleep(2000);
          } else if (this.serverType.equals(ServerType.SLOW)) {
            Thread.sleep(3000);
          }
          if (!p1eiades.tryLock(jobPath, LockService.LockMode.LOCK_EXCLUSIVE)) {
            System.out.println(identity + " can not get try lock and "
                + "number of tasks left for " + job.getJobName() + " : "
                + job.tasksLeft());
            break;
          }
          if (job.doTask()) {
            p1eiades.getLock(jobPath, LockService.LockMode.LOCK_EXCLUSIVE);
            System.out.println("===================== " + job.getJobName()
                + " is done =====================" + " by " + this.identity);
            doAnotherJob();
            break;
          } else {
            System.out.println("<<<" + identity + ">>>"
                + "number of tasks left for " + job.getJobName() + " : "
                + job.tasksLeft());
          }
        } catch (Exception e) {
        }
      }
    }

    private void doAnotherJob() {

      int jobIndex = 0;
      boolean foundAnotherJob = false;
      for (int index = 0; index < jobList.size(); ++index) {
        if (jobList.get(index).tasksLeft() > 0) {
          if (index >= jobIndex) {
            jobIndex = index;
            foundAnotherJob = true;
          }
        }
      }
      if (foundAnotherJob) {
        while (!stopRequested) {
          try {
            if (this.serverType.equals(ServerType.FAST)) {
              Thread.sleep(1000);
            } else if (this.serverType.equals(ServerType.NORMAL)) {
              Thread.sleep(2000);
            } else if (this.serverType.equals(ServerType.SLOW)) {
              Thread.sleep(3000);
            }
            if (!p1eiades.getLock(jobList.get(jobIndex).getJobName(),
                LockService.LockMode.LOCK_EXCLUSIVE)) {
              System.out.println(identity + " can not get lock "
                  + "number of tasks left for "
                  + jobList.get(jobIndex).getJobName() + " : "
                  + jobList.get(jobIndex).tasksLeft());
              break;
            }
            if (jobList.get(jobIndex).doTask()) {
              p1eiades.getLock(jobList.get(jobIndex).getJobName(),
                  LockService.LockMode.LOCK_EXCLUSIVE);
              System.out.println("===================== "
                  + jobList.get(jobIndex).getJobName()
                  + " is done =====================" + " by " + this.identity);
              break;
            } else {
              System.out.println("<<<" + identity + ">>>"
                  + "number of tasks left for "
                  + jobList.get(jobIndex).getJobName() + " : "
                  + jobList.get(jobIndex).tasksLeft());
            }
          } catch (InterruptedException e) {
            e.printStackTrace();
          } catch (IOException e) {
            e.printStackTrace();
          }
        }
      }
    }

    public void shutdown() {
      stopRequested = true;
    }

  }

  public void executeJob() {
    Thread server1 = new Thread(new TaskServer("server#1",
        ParallelServer.ServerType.FAST, path1));
    Thread server2 = new Thread(new TaskServer("server#2",
        ParallelServer.ServerType.NORMAL, path2));
    Thread server3 = new Thread(new TaskServer("server#3",
        ParallelServer.ServerType.SLOW, path3));
    server1.start();
    server2.start();
    server3.start();
  }

  public static void main(String args[]) {
    ParallelServer server = new ParallelServer();
    server.executeJob();
  }

}
